/*==============================================================================
 * AUTH: Matthew Baum, 9753095
 * DATE: April-06-2012
 * DESC: JUnit tests for the special cases on the Task Class.
 *==============================================================================*/

package SOEN_Phase_3;

import java.util.ArrayList;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import junit.framework.TestCase;
import static org.junit.Assert.*;
import java.io.PrintStream;
import java.io.File;


public class SpecialTaskTest extends TestCase
{
    // Name
    private static final String name = "Task Class Test";

    // Tasks
    private static Task parent1, parent2, child1, child2;

    public SpecialTaskTest()
    {
        super(name);
    }

    @BeforeClass
    public static void setUpClass() throws Exception
    {
    }

    @AfterClass
    public static void tearDownClass() throws Exception
    {
    }

    @Before
    @Override
    public void setUp()
    {
        // Init dummy Tasks
        parent1 = new Task(0, 120, "Parent 1", "Matt", 0, 20, false,
                new ArrayList<Task>(), new ArrayList<Task>());
        parent2 = new Task(1, 80, "Parent 2", "Matt", 0, 35,
                false, new ArrayList<Task>(), new ArrayList<Task>());
        child1 = new Task(2, 34, "Child 1", "Matt", 0, 0,
                false, new ArrayList<Task>(), new ArrayList<Task>());
        child2 = new Task(3, 45, "Child 2", "Matt", 0, 5,
                false, new ArrayList<Task>(), new ArrayList<Task>());
    }

    @After
    @Override
    public void tearDown()
    {
    }

    /**
     * Test the adding of a subtask:
     * - Subtask must appear in subtask list of parent
     * - Subtask must be marked as a subtask
     */
    @Test
    public void testAddSubtask()
    {
        // Add subtask to parent
        parent1.addSubtask(child1);

        // Verify that subtask was added
        boolean added = containsInSubtasks(parent1, child1);

        assertTrue("Subtask was NOT correctly added!", added);

        // Verify that subtask IS a subtask
        assertTrue("Subtask IS NOT a subtask!", child1.isSubtask());
    }

    /**
     * Test the removal of a subtask:
     * - Subtask must appear in subtask list of parent
     * - Subtask must be marked as a subtask
     */
    @Test
    public void testRemoveSubtask()
    {
        // Add
        parent1.addSubtask(child1);
        parent1.addSubtask(child2);

        // Remove
        parent1.removeSubtask(child2.getID());

        // Verify that subtask was removed
        boolean removed = ! containsInSubtasks(parent1, child2);

        assertTrue("Subtask was NOT correctly removed!", removed);

        // Verify that subtask IS NO LONGER a subtask
        assertFalse("Old subtask IS STILL a subtask!", child2.isSubtask());
    }

    /**
     * Test the adding of a subtask:
     * - A Task cannot subtask itself!
     */
    @Test
    public void testSelfSubtask()
    {
        // Add a task to itself
        parent1.addSubtask(parent1);

        // Test self-subtasking
        boolean added = containsInSubtasks(parent1, parent1);

        assertFalse("A Task has subtasked itself!", added);
    }

    /**
     * Test the adding of a subtask, multiple times:
     * - A task contains a subtask ONCE!
     */
    @Test
    public void testRedundantSubtask()
    {
        // Add subtask a few times
        parent1.addSubtask(child1);
        parent1.addSubtask(child1);
        parent1.addSubtask(child1);

        // Count occurrences of the gien child
        ArrayList<Task> subtasks = parent1.getSubtasks();
        int count = 0;

        for(int i = 0; i != subtasks.size(); ++i)
        {
            if(subtasks.get(i).getID() == child1.getID())
            {
                count++;
            }
        }

        assertFalse("Subtask was inserted multiple times!", count > 1);
    }

    /**
     * Test the adding of a subtask to multiple parents:
     * - This should NOT be allowed!
     */
    @Test
    public void testManyToOneSubtask()
    {
        // Add subtask to two parents
        parent1.addSubtask(child1);
        parent2.addSubtask(child1);

        // Detect child in parent 1
        boolean added1 = containsInSubtasks(parent1, child1);

        // Detect child in parent 2
        boolean added2 = containsInSubtasks(parent2, child1);

        assertFalse("Subtask was added to many parents!", added1 && added2);
    }

    /**
     * Test the assigning of subtasks in a circular fashion:
     * - This should NOT be allowed!
     */
    @Test
    public void testCircularSubtask()
    {
        // Add subtask to two parents
        parent1.addSubtask(child1);
        child1.addSubtask(child2);
        child2.addSubtask(parent1);

        // Detect parent1 in child2
        boolean addedCircular = containsInSubtasks(child2, parent1);

        assertFalse("A circular subtask relationship was allowed!", addedCircular);
    }

    /**
     * Test the assigning of a dependency.
     * This test tries to relate two unrelated tasks.
     *
     * NOTE: This is a UNIT test.
     */
    @Test
    public void testAddDependency()
    {
        // Create dependency
        boolean added = parent1.addDependency(parent2);

        assertTrue("Dependency was NOT added!", added);
    }

    /**
     * Test the removal of a dependency.
     *
     * NOTE: This is a UNIT test.
     */
    @Test
    public void testRemoveDependency()
    {
        // Create dependency
        boolean added = parent1.addDependency(parent2);
        assertTrue("Dependency was NOT added!", added);

        // Remove dependency
        boolean removed = parent1.removeDependency(parent2.getID());
        assertTrue("Dependency was NOT removed!", removed);
    }

    /**
     * Test the assigning of a dependency multiple times.
     *
     * NOTE: This is a UNIT test.
     */
    @Test
    public void testRedundantDependency()
    {
        // Create dependency
        boolean added = parent1.addDependency(child1);

        assertTrue("Dependency was NOT added!", added);

        // Add dependency again
        boolean redundant = parent1.addDependency(child1);

        assertFalse("Same dependency was added TWICE!", redundant);
    }

    /**
     * Test the assigning of dependencies in a
     * circular fashion.
     * This would make Tasks impossible to complete.
     *
     * NOTE: This is a UNIT test.
     */
    @Test
    public void testCircularDependency()
    {
        // Create dependencies
        boolean added = parent1.addDependency(child1);
        assertTrue("Dependency was NOT added!", added);

        added = child1.addDependency(child2);
        assertTrue("Dependency was NOT added!", added);

        // Add circular dependency
        boolean circular = child2.addDependency(parent1);
        assertFalse("Dependencies were added in a circular fashion!", circular);
    }

    /**
     * Test the assigning of a Task as a dependency
     * for multiple tasks.
     * This assigning is ACCEPTABLE, unlike multi-subtasking.
     *
     * NOTE: This is a UNIT test.
     */
    @Test
    public void testManyToOneDependency()
    {
        // Create dependency on P1
        boolean added = parent1.addDependency(child1);
        assertTrue("Dependency was NOT added!", added);

        // Create dependency on P2
        boolean multi = parent2.addDependency(child1);
        assertTrue("Dependency was NOT added to multiple supertasks!", multi);
    }

    /**
     * Verify that adding a subtask, also adds a dependency to the parent.
     * This is due to the fact that a parent CANNOT be
     * completed, without its subtasks being completed.
     *
     * NOTE: This is a MODULE test.
     */
    @Test
    public void testSubtaskDependency()
    {
        // Create subtask for P1
        parent1.addSubtask(child1);

        // Check that C1 is a dependency on P1
        ArrayList<Task> deps = parent1.getDependencies();

        boolean isDep = deps.contains(child1);

        assertTrue("Subtask did NOT result in the addition of a dependency!", isDep);
    }

    /**
     * Verify that removing a subtask, also removes a dependency
     * from the parent.
     *
     * NOTE: This is a MODULE test.
     */
    @Test
    public void testRemoveSubtaskRemoveDependency()
    {
        // Create subtask for P1
        parent1.addSubtask(child1);

        // Check that C1 is a dependency on P1
        ArrayList<Task> deps = parent1.getDependencies();

        boolean isDep = deps.contains(child1);

        assertTrue("Subtask did NOT result in the addition of a dependency!", isDep);

        // Remove the subtask
        parent1.removeSubtask(child1.getID());

        // Verify dependency was removed
        isDep = deps.contains(child1);
        assertFalse("Removing subtask left dependency intact!", isDep);
    }

    /**
     * Verify that removing a dependency, also removes a subtask
     * from the subtask list (if it ever existed there).
     *
     * NOTE: This is a MODULE test.
     */
    @Test
    public void testRemoveDependencyRemoveSubtask()
    {
        // Create subtask for P1
        parent1.addSubtask(child1);

        // Check that C1 is a dependency on P1
        ArrayList<Task> deps = parent1.getDependencies();

        boolean isDep = deps.contains(child1);

        assertTrue("Subtask did NOT result in the addition of a dependency!", isDep);

        // Remove the dependency
        boolean removed = parent1.removeDependency(child1.getID());
        assertTrue("Dependency was NOT removed!", removed);

        // Verify subtask was removed
        boolean subtaskRemoved = ! containsInSubtasks(parent1, child1);
        assertTrue("Subtask was NOT removed as a result of removing its "
                + "corresponding dependency!", subtaskRemoved);
    }

    /**
     * Verify that a task cannot depend on itself
     *
     * NOTE: This is a UNIT test.
     */
    @Test
    public void testSelfDependency()
    {
        // Add a Task to its own dependencies
        boolean added = parent1.addDependency(parent1);

        // Verify that the task was NOT added
        assertFalse("A Task was added to its OWN dependencies!", added);
    }

    // PRIVATE helper to determine if a task contains another in its subtasks
    private static boolean containsInSubtasks(Task parent, Task subtask)
    {
        ArrayList<Task> subtasks = parent.getSubtasks();

        for(int i = 0; i != subtasks.size(); ++i)
        {
            if(subtasks.get(i).getID() == subtask.getID())
            {
                return true;
            }
        }

        return false;
    }
}